Attribute Customization – Java Based Value Providers, Conditions and Validators


I had to look into how RTC Attribute Customization can be done using Java based Eclipse Extensions. This post shows how this works and provides with examples if you need to get started on the topic.

Update: Also look at A Custom Condition to Make Attributes Required or Read-Only by Role to see more on this kind of extensions.

The Lab 5 talks about using JavaScript for implementing custom providers. It also talks about limitations of the JavaScript API. The current JavaScript API limits you to accessing attributes of the current work items and is also very limited when working with more complex attributes. The JavaScript API in RTC 3.x and 4.x is so limited as it

  • Does not provide means to access extended user information such as team membership or roles
  • Does not allow to access links to Items such as other work items and other work items attributes
  • Does not allow to access information such as subscriptions or attachments

In addition there are few built in providers (see Lab 4 in the workshop) that can be used with list type attributes.

Recently I had to work for a customer request where the built in functionality and the available JavaScript API were clearly not sufficient to match the requirements. This gave me the opportunity to look into extending Attribute Customization using Java based extensions. I thought this would be a nice experience to share.

License and Download

As always, our lawyers reminded me to state, that the code in this post is derived from examples from Jazz.net as well as the RTC SDK. The usage of code from that example source code is governed by this license. Therefore this code is governed by this license, which basically means you can use it for internal usage, but not sell. Please also remember, as stated in the disclaimer, that this code comes with the usual lack of promise or guarantee.

The code presented in this post can be downloaded from here. The code is also available in the RTC Extensions Stream (RTC AttributeCustomization component) in the Jazz In Flight project @ JazzHub. The code shows some simple providers as an entry point and some more complex ones to show the potential.

Just Starting With Extending RTC?

If you just get started with extending Rational Team Concert, or create API based automation, start with the post Learning To Fly: Getting Started with the RTC Java API’s and follow the linked resources.

You should be able to use the code attached to this post in the development environment you set up in the Rational Team Concert Extensions Workshop and get your own extensions working there as well.

In this context, please also consider to at least read through the Process Enactment Workshop for the Rational solution for Collaborative Lifecycle Management lab 4 and lab 5 to understand how attribute customization works.

I looked into Attribute Customization for the RTC Process Enactment Workshop. You should look into Lab 4 if you don’t know how value providers work and are configured in RTC.

See JavaScript Attribute Customization – Where is the log file? for the log file location for debugging.

The code in this post is common code running in the Eclipse client and the RTC Server.

Finding Information in the SDK

Because I never did this kind of extension before, I started with reviewing the Wiki Article about Attribute Customization on Jazz.net. It is also a good idea to review existing java code to get some ideas.

This section describes how to find example code in the SDK, which is very important to be able to do this kind of work.

The following steps require the development environment to be set up like described in this post. This allows basically to search for references to the extension point and find the code that is involved.

First create a plugin project based on the suggestions in the Wiki Article. Then search for references to the Extension point.

Search for References to an extension point
Search for References to an extension point

The search will find some plugins extending this extension point.

Available extensions
Available extensions

Just double click the plugin com.ibm.workitem.common to open the plugin XML and review the extensions. You can switch to the Extensions tab in the plugin editor and get a better overview. You can navigate to the extending classes by simply looking their name up in the Extension Element Details section, copying the class name and running a Java Search for the declaration for the type. In the search result, find the result related to the SDK and open it in the package explorer.

Type in the search result - Show in Package Explorer
Type in the search result – Show in Package Explorer

Browse the package explorer and explore the SDK source of all the interesting classes. Open classes to look into the code and find out how they work.

Explorer the SDK code
Explore the SDK code

There are some very interesting examples that are worth looking into. Especially the RoleBasedUserProvider and the SecurityContextProvider.

Code to explore
Code to explore

I find the SecurityContextProvider interesting, because I am interested in automation to control the visibility of work items in the context of working with customers. I have a todo created to try to find time to create my own implementation.

Example Implementations

As usual I started with some very simple examples.

  • A default value provider for string type attributes that returns some text
  • A calculated value provider for string and string list type attributes that returns some text
  • A validator for string type attributes that looks if the string contains some sub string and is configurable in the process configuration

These are available out of the box, but as examples easy enough to do without having to fight with more API and I learned a lot from them for reasons I did not expect.

Example Implementations: attributeType

The first decision necessary when following the suggestions in the Wiki Article and creating the extension, is to decide which type the provider should work for. The Article provides a list of examples for the type ID’s, but this is by no means complete. I am not sure yet, where to find the complete list. This section of a Wiki article could be a good starting point. You can look up examples in the extensions mentioned above. Another Theme seems to be to add List in case you want a list type attribute. For example if the return type is string and you want to support a list attribute, the attribute type is stringList.

Also be aware you can and should add several attribute type definitions if you want the extension to support more than one type. String provider are typical in this case. If you want to be able to select them for all string type attributes, define small, medium and large string like below.

ExtensionPointDefinitonsYou can leave out the settings for isDefault and requiresConfiguration in theExtension Elements Details.

API Available in Providers

All provider implementations get called with the following data:

  • IAttribute attribute, the attribute the provider is called for (not available for conditions)
  • IWorkItem workItem, the workitem context the provider is called for
  • IWorkItemCommon workItemCommon, as an entry point to get more API services
  • IConfiguration configuration, the configuration in the process XML related to this provider
  • IProgressMonitor monitor, the progress monitor (can be null)

You can use the workItemCommon to get other common services. Since the providers run in the client as well as in the server the API you have access to seems to be the API common to both. I found IAuditableCommon and IAuditableCommonProcess so far.

Default Value Provider

The code for a default value provider must implement the interface com.ibm.team.workitem.common.internal.attributeValueProviders.IDefaultValueProvider. The type is correctly generated if you follow the Wiki Article. The implementation here is very simple and just returns a string.

The code below shows a very simple default value provider.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.workitem.attribute.customization.providers;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IDefaultValueProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

public class SampleStringDefaultProvider implements IDefaultValueProvider {

	public SampleStringDefaultProvider() {
	}

	@Override
	public String getDefaultValue(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		return "String Default Value";
	}
}

Calculated Value Provider

The next example is a simple calculated value provider that returns a string containing the time since 1970 in milliseconds. The interface implemented is com.ibm.team.workitem.common.internal.attributeValueProviders.IValueProvider. Please note, all examples below are typed and specify the return type e.g. .

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.workitem.attribute.customization.providers;

import java.util.Date;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValueProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

public class SampleStringCalculatedValueProvider implements IValueProvider {

	public SampleStringCalculatedValueProvider() {
	}

	@Override
	public String getValue(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		Date date = new Date();
		return "String Calculated Value: "+date.getTime();
	}
}

Again, very basic to show and understand the concept.

A second example is showing a calculated value for a stringList attribute type. The difference is that it returns a List that contains several values.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.workitem.attribute.customization.providers;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValueProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

public class SampleStringListProvider implements IValueProvider {

	public SampleStringListProvider() {
	}

	@Override
	public List getValue(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		List result = new ArrayList();
		result.add("Test 1");
		result.add("Test 2");
		result.add("Test 3");
		return result;
	}
}

Validator

The last simple example is a validator that validates if a text field contains a certain sub string. It implements the interface com.ibm.team.workitem.common.internal.attributeValueProviders.IValidator. The validator, configured for a string attribute, by default searches for the search string “Test” in the attribute value and issues a warning.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.workitem.attribute.customization.providers;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValidator;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

/**
 * Validates if a string is contained in the text.
 * 
 * Configure in Process XML
 * 
 * 
 *
 */
public class SampleStringContainsSubstring implements IValidator {

	private static final String SMAPLE_STRING_CONTAINS_SUBSTRING = "com.ibm.js.team.workitem.attribute.customization.providers.SmapleStringContainsTestValidator";
	private static final String REQUIRED_CONTENT = "requiredContent";
	private static final String WARNING_LEVEL = "warning";
	private static final String SEVERITY = "severity";
	private static final String VALIDATION_OK_STATUS = "ValidationOK";
	private static final String CONFIGURATION = "configuration";

	private String fSearchString;
	private int fValidationSeverity;

	public SampleStringContainsSubstring() {
	}

	@Override
	public IStatus validate(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		getConfiguration(configuration);

		String value = (String) attribute.getValue(workItemCommon.getAuditableCommon(), workItem, monitor);
		if(value.indexOf(fSearchString)>=0){
			return new Status(Status.OK, SMAPLE_STRING_CONTAINS_SUBSTRING,VALIDATION_OK_STATUS);
		}
		return new Status(fValidationSeverity, SMAPLE_STRING_CONTAINS_SUBSTRING, "Validation: String must contain '"+fSearchString+"'");
	}

	private void getConfiguration(IConfiguration configuration) {
		fSearchString = "Test";
		fValidationSeverity=Status.WARNING;
		IConfiguration parameters= configuration.getChild(CONFIGURATION);
		if(null!=parameters){ // We got a configuration
			String customSearchString=parameters.getString(REQUIRED_CONTENT);
			if(null!=customSearchString)
				fSearchString=customSearchString;
			String configuredValidationSeverity=parameters.getString(SEVERITY);
			if(null!=configuredValidationSeverity)
				fValidationSeverity =  configuredValidationSeverity.equalsIgnoreCase(WARNING_LEVEL)?Status.WARNING:Status.ERROR;
		}
	}
}

The code is a bit more complex, because it allows to configure the test string and the severity in the process XML. The configuration and defaults are read in getConfiguration() and stored in fields. Then the attribute value is read and tested if it contains the substring. If not, a message is created that shows in the UI. Based on the configuration the severity is of level error or warning. Error would allow to prevent saving a work item together with the Attribute Validation work item save precondition. The configuration would look like this:

Validator configuration in the process XML
Validator configuration in the process XML

Condition

I could not come up with an easy condition. Instead I looked into more complex things next. The next examples I did, were focused to automate something based on the role of a user.

The following condition provides information, if the user has a set of roles in the process area the workitem is owned by. It can for instance be used to make attributes mandatory or read only based on this information. The condition is fully configurable in the process XML and can manage one or more roles.

Please be aware that the extension point schema has an issue. If you create the class to handle the extension, you get an error. Create it manually or fix it. The class you create should implement com.ibm.team.workitem.common.internal.attributeValueProviders.ICondition.

Please see the code blow for details.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.workitem.attribute.customization.providers;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.IContributorHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.ICondition;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.model.IWorkItem;

/**
 * Condition that checks if the user editing the work item has a specific role
 * 
 * Configure in Process XML
 * 
 * 
 * 
 *
 */
public class SampleCurrentUserHasRoleCondition implements ICondition {

	/* (non-Javadoc)
	 * @see com.ibm.team.workitem.common.internal.attributeValueProviders.ICondition#matches(com.ibm.team.workitem.common.model.IWorkItem, com.ibm.team.workitem.common.IWorkItemCommon, com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration, org.eclipse.core.runtime.IProgressMonitor)
	 * 
	 * Return true if the user has the specified role, else false
	 */
	@Override
	public boolean matches(IWorkItem workItem, IWorkItemCommon workItemCommon,
			IConfiguration configuration, IProgressMonitor monitor)
			throws TeamRepositoryException {
		List lookupRoles = ContributorUtil.getConfiguration(configuration);
		IContributorHandle user= workItemCommon.getAuditableCommon().getUser();
		return ContributorUtil.hasRole(workItem, user, lookupRoles, workItemCommon, monitor);
	}
}

That is short. The reason is, that all the code is in the class ContributorUtil. I was able to reuse that code in several ways and ended up collecting it in the utility class. The method getConfiguration() basically gets the roles to look for from the process configuration.

Then the code gets the IContributorHandle of the current user and the method hasRole check if the user has the role.

The code for the ContributorUtil is below.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.workitem.attribute.customization.providers;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.process.common.IProcessArea;
import com.ibm.team.process.common.IProcessAreaHandle;
import com.ibm.team.process.common.IRole;
import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.IContributorHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IAuditableCommonProcess;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.ItemProfile;

/**
 * Utility class to provide contributor related utilities
 *
 */
public class ContributorUtil {

	private static final String DEFAULT_ROLE_TEAM_MEMBER = "ScrumMaster";
	static final String ROLE = "role";
	static final String PROCESSAREA = "processArea";

	/**
	 * Returns a list of members that have a certain role in the process area a work item is filed against
	 * 
	 * @param workItem
	 * @param lookupRoles
	 * @param workItemCommon
	 * @param monitor
	 * @return a list of contributor handles
	 * @throws TeamRepositoryException
	 */
	public static List findProcessAreaMembersByRole(IWorkItem workItem, Collection lookupRoles,
			IWorkItemCommon workItemCommon, IProgressMonitor monitor)
			throws TeamRepositoryException {
		IProcessAreaHandle processAreaHandle = workItemCommon.findProcessArea(workItem, monitor);
		IProcessArea processArea = (IProcessArea) workItemCommon.getAuditableCommon().resolveAuditable(processAreaHandle,
				ItemProfile.PROCESS_AREA_DEFAULT,monitor);
		IAuditableCommonProcess auditableCommonProcess= workItemCommon.getAuditableCommon().getProcess(processArea, monitor);
		IContributorHandle[] userhandles = auditableCommonProcess.getContributorsWithRole(processArea.getMembers(), processArea, lookupRoles.toArray(new String[lookupRoles.size()]), monitor);
		return workItemCommon.getAuditableCommon().resolveAuditables(Arrays.asList(userhandles), ItemProfile.CONTRIBUTOR_DEFAULT, monitor);
	}

	/**
	 * Returns true if the user working on the work item has a specified role in the process area 
	 * the work item is filed against
         *
	 * @param workItem
	 * @param user
	 * @param lookupRoles
	 * @param workItemCommon
	 * @param monitor
	 * @return true if the user has the specified role, else false
	 * @throws TeamRepositoryException
	 */
	public static boolean hasRole(IWorkItem workItem, IContributorHandle user, List lookupRoles,
			IWorkItemCommon workItemCommon, IProgressMonitor monitor)
			throws TeamRepositoryException {
		IProcessAreaHandle processAreaHandle = workItemCommon.findProcessArea(workItem, monitor);
		IProcessArea processArea = (IProcessArea) workItemCommon.getAuditableCommon().resolveAuditable(processAreaHandle,
				ItemProfile.PROCESS_AREA_DEFAULT,
				monitor);
		IAuditableCommonProcess auditableCommonProcess= workItemCommon.getAuditableCommon().getProcess(processArea, monitor);
		Collection roles = auditableCommonProcess.getContributorRoles(user, processArea, monitor);
		for (IRole aRole : roles) {
			if(lookupRoles.contains(aRole.getId())){
				return true;
			}
		}
		return false;
	}

	/**
	 * Reads the role configuration
	 * 
	 * Configure in Process XML
	 * 
	 * 
	 * 
	 * 
	 * @param configuration
	 * @return
	 */
	public static List getConfiguration(IConfiguration configuration) {
		ArrayListlookupRoles=new ArrayList();

		List processAreaConfigurations= configuration.getChildren(PROCESSAREA);
		if(null!=processAreaConfigurations&&!processAreaConfigurations.isEmpty()){ // We got a configuration
			for (IConfiguration roleConfiguration : processAreaConfigurations) {
				String foundRole=roleConfiguration.getString(ROLE);
				if(foundRole!=null){
					lookupRoles.add(foundRole);
				}
			}
		} else {
			lookupRoles.add(DEFAULT_ROLE_TEAM_MEMBER);
		}
		return lookupRoles;
	}
}

The method ContributorUtil.hasRole() basically gets the process area and then gets the roles of the user in this process area and then checks if one of the roles matches a configured role.

ContributorUtil.getConfiguration() basically reads the process XML configured for the extension and returns a list of roles configured. By default and unconfigured it looks for Team Member. Please be aware it looks for the role ID and not the display name.

Value Set Provider

As an example for a value set provider that implements com.ibm.team.workitem.common.internal.attributeValueProviders.IValueSetProvider see the example that provides a set of users with roles. The code looks as shown below:

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.workitem.attribute.customization.providers;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValueSetProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

/**
 * This ValueSet Provider returns a list of users that have the configured role 
 * in the process area the work item is filed against
 * 
 * Configure in Process XML
 * 
 *   
 * 
 *
 */
public class SampleUsersWithRoleValueSetProvider implements IValueSetProvider {

	@Override
	public List<? extends IContributor> getValueSet(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		List lookupRoles = ContributorUtil.getConfiguration(configuration);
		return ContributorUtil.findProcessAreaMembersByRole(workItem, lookupRoles,
				workItemCommon, monitor);
	}
}

It uses the same methods provided by the ContributorUtil class used by the other more complex providers and conditions.

It works for contributor as well as contributorList attribute types.

Additional Providers

ContributorUtil.findProcessAreaMembersByRole() basically finds the users that have a role matching the list of roles in the process area and returns the users as list. I used this in several other providers.

  • Sample User With Role In ProcessArea Default provides a default value for one user with matching roles (the first of the returned users) for a user type attributes
  • Sample User With Role In ProcessArea Default provides a default value for all users with matching roles  for a userList type attributes
  • Sample User With Role In ProcessArea Calculated Value returns the first user in the list of found users with one of the roles back as calculated value for user type attributes
  • Sample Users With Role In ProcessArea Calculated Value returns all matching users found for userList attribute types
  • Sample Users With Role In ProcessArea provides users with matching roles

The image below shows the classes that implement the required interfaces.

Additional Providers for users with roles
Additional Providers for users with roles

All the provider classes look very similar. They implement one or more interfaces and use the utility class to get the required data. Here an example:

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.workitem.attribute.customization.providers;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IDefaultValueProvider;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValueProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

/**
 * Implements a value provider that returns the users that have a specific role in the process
 * area the work item is filed against.
 * 
 * Configure in Process XML
 * 
 * 
 * 
 * 
 *
 */
public class SampleUserWithRoleValueProvider implements IDefaultValueProvider,IValueProvider {

	public SampleUserWithRoleValueProvider() {
	}

	@Override
	public IContributor getValue(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {

		List lookupRoles = ContributorUtil.getConfiguration(configuration);
		List users = ContributorUtil.findProcessAreaMembersByRole(workItem, lookupRoles,
				workItemCommon, monitor);
		if(!users.isEmpty()){
			return users.get(0);
		}
		return null;
	}

	@Override
	public IContributor getDefaultValue(IAttribute attribute,
			IWorkItem workItem, IWorkItemCommon workItemCommon,
			IConfiguration configuration, IProgressMonitor monitor)
			throws TeamRepositoryException {

		return getValue(attribute, workItem, workItemCommon, configuration, monitor);
	}
}

All these providers can be configured for one or more roles in the process XML like shown below. The configuration is within the entry for the provider as described in the Jazz.net Wiki entry about attribute customization.

Provider configured with two roles
Provider configured with two roles

Deploying

I ran this in a test environment set up as described here.

The downloadable code contains a feature and an update site project. Follow the Rational Team Concert Extension Workshop and the instructios in the Wiki Article about Attribute Customization to deploy the code on the client and the server.

Related Posts

Summary

The code above is a good starting point if you want to create your own providers. There are more capabilities that you can use with Java compared to JavaScript. What data really is accessible remains to be seen. I still have to look if it would be possible to for example access SCM components and the like. I hope it is work reading and helps some users out there to save some time to get started with this topic.

20 thoughts on “Attribute Customization – Java Based Value Providers, Conditions and Validators

  1. Hi, Ralph. I want to get a attribute’s previous value and current value on the workitem. Where Can I find this example or API?Thank you!

    • You can access the previous states of an auditable using

      IAuditable.getMergePredecessorState();
      IAuditable.getPredecessorState();

      An IAuditable can have two predecessors and in case of a work item you can then access the data in the predecessors that exist and compare values.

      In an advisor or follow up action/participant you get the old state and the new state of the work item, which are basically the predecessor and the new state.

      You can access the data on the old state and compare it to what you have in the new state. The old state does only exist if this is not a new work item.

  2. Hello Ralph,

    I am trying to write a java based value set in which the values for a certain attribute is dependent on a particular state.
    i have successfully created a site file of this value provider and deployed it in the ccm/site folder along with the proper ini file.
    i Checked in the Provision status the feature is being installed but i am clueless from where i shall trigger the value provider. Please provide your Inputs.

    Thanks.
    Manju S

    • First, you use Jetty to debug if it works. You don’t just deploy it if you haven’t done that step.

      The value provider should show up as choice in the Attribute customization section of the process configuration.

      It has to be deployed to the Eclipse client as well, to show up there.

  3. Hello Ralph,

    I want to write an advisor that is similar to the behavior presented in “Required Attributes for Type and State”, but I need that behavior to happen before the save operation in order that when the save is concluded the attribute can’t be modified.

    I have tried with what is shown in this post, however, I’m not getting the red asterisks in the attributes as in “Required Attributes for Type and State”.

    Where I can find how to do this in the API?

      • Thanks for your article Ralph.
        I need to get the User, state, User roles and state roles values from workitem, will get this values from matches method using the following article “https://rsjazz.wordpress.com/2015/06/19/a-custom-condition-to-make-attributes-required-or-read-only-by-role/”.

        Want to get these values when click the save button, so now i am using the run methid from operationadvisors interface for this function.

        matches() method is the entry point of this class? or will call the matches method from run method?

        If call the matches method from run method, how can pass the Iconfiguration value as parameter to matches method from run method.

        Please clarify me, I am struck up in this for last ten days.

      • This does not make any sense at all. If you want to use parts of that code in your advisor, copy the relevant code parts over and use them there.

        As explained before the code in the condition plugin is an extension to RTC and assumes to be called by RTC in the correct way, which is not available in your participant. So you basically reuse parts of the code you need by copying it where you need it. It does not make sense to call the matches method.

      • This article is great.
        I have one clarification in that and i below mention that,
        1. I have an access in State A. I have to save or assigns the StateA to StateB. But I don’t have the access in the StateB. So as per the article I can’t able to save the state from StateA to StateB.
        2. After assigned to StateB(I dont have access in the state B) how to make as the status attributes as read only attributes as per the user and roles while refresh the status or after the click the state button.
        Please share your Info. to achieve this.

      • I am sorry, but I can’t figure out what you are asking for and what you want to achive. Your description does not make sense.
        If your intention would be to make the state attribute read only, this does not make any sense to me.

  4. Hello Mr. Ralph,

    I have downloaded source code which this article attached. I deployed site file in server and client as well. plugins deployed successfully but when i try to set default value the value is not setting.
    I am using RTC 6.0. kindly request you to help if i am missing somewhere.

    Thanks in advance

    • Sorry, but you will have to follow the section Just Starting With Extending RTC? and setup the SDK and debug and get some experience with deploying and debugging. As your comment lacks any useful information.

      The downloadable code lacks the provision profile folder and the provision profile .ini file. So you might think you have deployed but maybe you haven’t? Can you select the value providers? Are there errors in the logs? If you want to discus this and provide more information, please ask on Jazz.net.

      Ralph

      • Also please note, that in addition to deploying the provider on the server, you also need to deploy the provider to all Eclipse clients to be able to configure and have them being available if you create work items in Eclipse.

      • I deployed the plugins correctly int server and in eclipse client.I am able to see the “Sample String Default Provider”. When i opened ccm.log i am getting ” Default value provider not found: com.ibm.js.team.workitem.attribute.customization.providers.SampleStringDefaultProvider”.

      • I just ran the code on Jetty and it works, so I have to assume you did not deploy it correctly. so something is wrong in your deployment.

  5. is it possible to create a custom attribute (input data to the custom plugin) while configuring the plugin under adding the plugin as per user role in operation behaviour like for state change build we providing id, build (like this weather it possible to create)

    • I don’t understand what you are asking. If you ask if it is possible to create a customization UI, that is theoretically possible for Java based attribute customization. There is no documentation however and it stores the data in the attribute customization configuration (XML) that is already available.

  6. Hi Ralph –

    I’m trying to explore attribute customization via plug-ins and have hit a wall. I’m working in a 7.0.2 SR1 environment.

    The instructions for creating the plug-in project (https://jazz.net/wiki/bin/view/Main/AttributeCustomization#Providing_new_customizations_imp)
    including adding com.ibm.team.repository.common as a dependency. Seems straightfoward, but I can’t add *just* com.ibm.team.repository.common; only some plugins that are subpackages of com.ibm.team.repository.common. Here’s a screenshot: https://app.screencast.com/ZxC1m5r5WWZTy
    The com.ibm.team.repository.common plug-in appears in the Plug-ins view, , but the plug-in selection dialog doesn’t load it.

    Any thoughts what I need to do?

    • I run into the same issue. I do not know why at the moment. I would suggest you open a case with support, so there is a bit more focus on the SDK.

      As a work around, you can import the projects attached to this blog post. You can then open the MANIFEST.MF file in a text editor and copy/paste the line com.ibm.team.repository.common;bundle-version=”1.3.1200″,
      into your MANIFEST.MF.

      I searched for the plugin and it is definitely available in the SDK. Not sure why it does not show up.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.